﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Web.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Xpedite.Extensions;
using Xpedite.Localization;
using Xpedite.Resources;
using Xpedite.Resources.Definitions;

/* Copyright (c) 2010 CBaxter
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE. 
 */

namespace Xpedite.Test.Extensions
{
  [TestClass]
  public class HtmlWriterExtensionsTest
  {
    [TestMethod]
    public void TestRenderCssStyleSheetLink()
    {
      var resourceUrl = new Mock<IResourceUrl>(MockBehavior.Strict);

      resourceUrl.SetupGet(mock => mock.VirtualPath).Returns(@"/VirtualDirectory/File.css");

      Assert.AreEqual(
          "<link type=\"text/css\" rel=\"stylesheet\" href=\"/VirtualDirectory/File.css\" />",
          RenderHtmlFor(writer => writer.RenderCssStyleSheetLink(resourceUrl.Object))
        );
    }

    [TestMethod]
    public void TestRenderJavaScriptSourceScript()
    {
      var resourceUrl = new Mock<IResourceUrl>(MockBehavior.Strict);

      resourceUrl.SetupGet(mock => mock.VirtualPath).Returns(@"/VirtualDirectory/File.css");

      Assert.AreEqual(
          "<script type=\"text/javascript\" src=\"/VirtualDirectory/File.css\">" + Environment.NewLine + Environment.NewLine + "</script>",
          RenderHtmlFor(writer => writer.RenderJavaScriptSourceScript(resourceUrl.Object))
        );
    }

    [TestMethod]
    public void TestRenderCssSprite()
    {
      var definition = CreateResourceDefinition();
      var container = new Mock<IResourceContainer>(MockBehavior.Strict);

      definition.Setup(mock => mock.GetResource("MyImageName")).Returns(container.Object);
      definition.SetupGet(mock => mock.ReferenceName).Returns("MySpriteName");
      container.SetupGet(mock => mock.Resource).Returns(new ImageResource { Size = new Size(15, 20) });

      Assert.AreEqual(
          "<img id=\"MyId\" alt=\"MyAltText\" title=\"MyAltText\" class=\"xpedite-css-MySpriteName MyImageName\" src=\"/xpedite.sprite.xcr\" width=\"15\" height=\"20\" />",
          RenderHtmlFor(writer => writer.RenderCssSprite(definition.Object, "MyImageName", "MyAltText", "MyId"))
        );
    }

    [TestMethod]
    public void TestRenderCssSpriteWhereResourceNotImage()
    {
      var definition = CreateResourceDefinition();
      var container = new Mock<IResourceContainer>(MockBehavior.Strict);

      definition.Setup(mock => mock.GetResource("MyImageName")).Returns(container.Object);
      definition.SetupGet(mock => mock.ReferenceName).Returns("MySpriteName");
      container.SetupGet(mock => mock.Resource).Returns(new Resource());

      using (var stringWriter = new StringWriter())
      using (var htmlTextWriter = new HtmlTextWriter(stringWriter))
      {
        Expect.Exception(
          () => htmlTextWriter.RenderCssSprite(definition.Object, "MyImageName", "MyAltText", "MyId"),
          new XpediteException(String.Format(ExceptionMessages.ImageResourceExpected, "MyImageName", "MySpriteName"))
        );
      }
    }

    [TestMethod]
    public void TestRenderCompositeDefinitions()
    {
      var definition1 = CreateResourceDefinition();
      var definition2 = CreateResourceDefinition();

      using (var stringWriter = new StringWriter())
      using (var htmlTextWriter = new HtmlTextWriter(stringWriter))
      {
        definition1.Setup(mock => mock.EmitResource(htmlTextWriter));
        definition2.Setup(mock => mock.EmitResource(htmlTextWriter));

        htmlTextWriter.RenderCompositeDefinitions(new[] { definition1.Object, definition2.Object });
      }
    }

    [TestMethod]
    public void TestRenderDebuggableDefinitionsWithCssDefinition()
    {
      var definition1 = CreateResourceDefinition(ResourceType.Css, "Css1", 2);
      var definition2 = CreateResourceDefinition(ResourceType.Css, "Css2", 2);

      using (var stringWriter = new StringWriter())
      using (var htmlTextWriter = new HtmlTextWriter(stringWriter))
      {
        htmlTextWriter.RenderDebuggableDefinitions(new[] { definition1.Object, definition2.Object });

        Assert.AreEqual(
          "<link type=\"text/css\" rel=\"stylesheet\" href=\"/Css1/File0.css\" />" +
          "<link type=\"text/css\" rel=\"stylesheet\" href=\"/Css1/File1.css\" />" +
          "<link type=\"text/css\" rel=\"stylesheet\" href=\"/Css2/File0.css\" />" +
          "<link type=\"text/css\" rel=\"stylesheet\" href=\"/Css2/File1.css\" />",
          stringWriter.ToString()
        );
      }
    }

    [TestMethod]
    public void TestRenderDebuggableDefinitionsWithJsDefinition()
    {
      var definition1 = CreateResourceDefinition(ResourceType.JavaScript, "Js1", 2);
      var definition2 = CreateResourceDefinition(ResourceType.JavaScript, "Js2", 2);

      using (var stringWriter = new StringWriter())
      using (var htmlTextWriter = new HtmlTextWriter(stringWriter))
      {
        htmlTextWriter.RenderDebuggableDefinitions(new[] { definition1.Object, definition2.Object });

        Assert.AreEqual(
          "<script type=\"text/javascript\" src=\"/Js1/File0.css\">" + Environment.NewLine + Environment.NewLine +   
          "</script><script type=\"text/javascript\" src=\"/Js1/File1.css\">" + Environment.NewLine + Environment.NewLine +   
          "</script><script type=\"text/javascript\" src=\"/Js2/File0.css\">" + Environment.NewLine + Environment.NewLine +   
          "</script><script type=\"text/javascript\" src=\"/Js2/File1.css\">" + Environment.NewLine + Environment.NewLine +   
          "</script>",
          stringWriter.ToString()
        );
      }
    }

    [TestMethod]
    public void TestRenderDebuggableDefinitionsWithUnknownDefinition()
    {
      var definition1 = CreateResourceDefinition(ResourceType.Unknown, "Js1", 2);
      var definition2 = CreateResourceDefinition(ResourceType.Unknown, "Js2", 2);

      using (var stringWriter = new StringWriter())
      using (var htmlTextWriter = new HtmlTextWriter(stringWriter))
      {
        definition1.Setup(mock => mock.EmitResource(htmlTextWriter));
        definition2.Setup(mock => mock.EmitResource(htmlTextWriter));

        htmlTextWriter.RenderDebuggableDefinitions(new[] { definition1.Object, definition2.Object });

        Assert.AreEqual(String.Empty, stringWriter.ToString());
      }
    }

    private static String RenderHtmlFor(Action<HtmlTextWriter> action)
    {
      using (var stringWriter = new StringWriter())
      using (var htmlTextWriter = new HtmlTextWriter(stringWriter))
      {
        action.Invoke(htmlTextWriter);

        return stringWriter.ToString();
      }
    }

    private static Mock<IResourceDefinition> CreateResourceDefinition()
    {
      return CreateResourceDefinition(ResourceType.Css, "ResourceDefinition", 0);
    }

    private static Mock<IResourceDefinition> CreateResourceDefinition(ResourceType resourceType, String referenceName, Int32 resourceCount)
    {
      var definition = new Mock<IResourceDefinition>(MockBehavior.Strict);
      var resources = new List<IResourceContainer>();

      for (var i = 0; i < resourceCount; i++)
      {
        var resourceUrl = new Mock<IResourceUrl>(MockBehavior.Strict);
        var resourceContainer = new Mock<IResourceContainer>(MockBehavior.Strict);

        resourceUrl.SetupGet(mock => mock.VirtualPath).Returns(String.Format("/{0}/File{1}.css", referenceName, i));
        resourceContainer.SetupGet(mock => mock.ResourceUrl).Returns(resourceUrl.Object);
        resourceContainer.SetupGet(mock => mock.ResourceType).Returns(resourceType);

        resources.Add(resourceContainer.Object);
      }

      definition.SetupGet(mock => mock.ResourceType).Returns(resourceType);
      definition.SetupGet(mock => mock.Resources).Returns(resources);

      return definition;
    }
  }
}
